#include "qdownloadhandler.h"

QDownloadHandler::QDownloadHandler(QObject *parent) : QObject(parent)
{
    serial = new QSerialPort(this);
    chipIndex = 0;
    _isDownloading = false;
    _isReadProtect = false;
}

QDownloadHandler::~QDownloadHandler()
{
    serial->close();
}

void QDownloadHandler::setPortName(const QString &portName)
{
    _portName = portName;
}

void QDownloadHandler::setFileName(const QString &fileName)
{
    _fileName = fileName;
}

void QDownloadHandler::setChipName(const QString &chipName)
{
    _chipName = chipName;
    for(quint32 i = 0; i < sizeof(chipType)/sizeof(chipType[0]); i++)
    {
        if(QString(chipType[i].name) == chipName)
        {
            chipIndex = i;
        }
    }
}

void QDownloadHandler::setBaudrate(int baudrate)
{
    _baudrate = baudrate;
}

void QDownloadHandler::setReadProtcet(bool enable)
{
    _isReadProtect = enable;
}

bool QDownloadHandler::isDownloading()
{
    return _isDownloading;
}

void QDownloadHandler::download()
{
    int ret = 0;
    quint32 address = 0X08000000;
    quint32 nsend = 0;
    quint8 cmds[32];
    quint8 num, protocolVersion;
    quint8 pid[64];
    quint8 lenpid = 0;
    quint8 uid[64];
    quint8 lenuid = 0;
    quint32 flashCapacity = 0;
    QString str;
    QByteArray fileData;
    QByteArray md5;
    int nread = 0;

    QFile file(_fileName);

    _isDownloading = true;

    emit this->information(">>> 检查固件..." + _fileName);
    if(!file.exists())
    {
        ret = -1;
        goto __err;
    }

    emit this->information(">>> 打开固件..." + _fileName);
    if(!file.open(QIODevice::ReadOnly))
    {
        ret = -1;
        goto __err;
    }

    fileData = file.readAll();
    file.close();
    emit this->information(QString::asprintf(">>> 固件大小：%d字节", fileData.size()));

    // 初始化串口
    serial->setPortName(_portName);
    serial->setBaudRate(_baudrate);
    serial->setDataBits(QSerialPort::Data8);
    serial->setStopBits(QSerialPort::OneStop);
    serial->setParity(QSerialPort::EvenParity);
    serial->setFlowControl(QSerialPort::NoFlowControl);
    if(!serial->open(QSerialPort::ReadWrite))
    {
        emit this->error(QString("串口打开失败！\r\n请检查[%1]是否存在或被占用！").arg(_portName));
        ret = -2;
        goto __err;
    }

    // 连接设备
    emit this->information(">>> 连接芯片...");
    if(ret < enterIspMode())
    {
        ret = -3;
        goto __err;
    }

    // 取消读保护
    emit this->information(">>> 取消读保护...");
    if(setReadoutProtect(false) < 0)
    {
        ret = -4;
        goto __err;
    }

    // 连接设备
    emit this->information(">>> 连接芯片...");
    if(enterIspMode() < 0)
    {
        ret = -5;
        goto __err;
    }

    // 取消写保护
    emit this->information(">>> 取消写保护...");
    if(setWriteProtect(false) < 0)
    {
        ret = -6;
        goto __err;
    }

    // 连接设备
    emit this->information(">>> 连接芯片...");
    if(enterIspMode() < 0)
    {
        ret = -7;
        goto __err;
    }

    // 读取支持的命令
    emit this->information(">>> 获取可支持的命令...");
    if(readCmd(&protocolVersion, cmds, &num) < 0)
    {
        ret = -8;
        goto __err;
    }
    str.clear();
    for(int i = 0; i < num; i++)
    {
        str += QString::asprintf("%02X ", (quint8)cmds[i]);
    }
    emit this->information(">>> " + str);
    emit this->information(QString::asprintf(">>> 协议版本：%d.%d", (protocolVersion >> 4) & 0x0F, protocolVersion & 0x0F));

    // 读取PID
    emit this->information(">>> (PID)获取产品ID...");
    if(readPID(pid, &lenpid) < 0)
    {
        ret = -9;
        goto __err;
    }

    str.clear();
    for(int i = 0; i < lenpid; i++)
    {
        str += QString::asprintf("%02X", pid[i]);
    }
    emit this->information(">>> " + str);

    // 读取UID
    emit this->information(">>> (UID)获取96位唯一ID...");
    if(readUID(chipType[chipIndex].uniqueId, uid, &lenuid) < 0)
    {
        ret = -10;
        goto __err;
    }

    str.clear();
    for(int i = 0; i < lenuid; i++)
    {
        str += QString::asprintf("%02X", uid[i]);
    }
    emit this->information(">>> " + str);

#if 0
    // MD5加密
    emit this->information(">>> (MD5)加密96位唯一ID...");
    md5 = QCryptographicHash::hash(QByteArray((char *)uid, lenuid), QCryptographicHash::Md5);
    str.clear();
    for(int i = 0; i < md5.size(); i++)
    {
        str += QString::asprintf("%02X", (quint8)md5[i]);
    }
    emit this->information(">>> " + str);
#endif

    // 读取FLASH容量
    emit this->information(">>> 获取FLASH容量...");
    if(readFlashCapacity(chipType[chipIndex].flashCapacity, &flashCapacity) < 0)
    {
        ret = -11;
        goto __err;
    }
    emit this->information(QString::asprintf(">>> %dKB", flashCapacity));

    // 检查文件是否过大
    if((flashCapacity * 1024) < (quint32)fileData.size())
    {
        emit this->information(">>> 固件过大！");
        goto __err;
    }

    // 擦除全片
    emit this->information(">>> 全片擦除...");
    if(eraseFull() < 0)
    {
        ret = -12;
        goto __err;
    }

    // 发送固件
    emit this->information(">>> 开始烧录...");
    while(nread < fileData.size())
    {
        int packetSize = fileData.size() - nread > 256 ? 256 : fileData.size() - nread;
        emit this->information(QString::asprintf(">>> 本次写入：%3d字节，  写入地址：0x%08X，  已经写入：%d字节 ...", packetSize, address, nread));
        if(writeBlock(address, fileData.data() + nread, packetSize) < 0)
        {
            ret = -13;
            goto __err;
        }
        address += packetSize;
        nsend += packetSize;
        nread += packetSize;
        emit this->progress((float)(nsend-1) / (float)file.size() * 100);
    }

    // 设置读保护
    if(_isReadProtect)
    {
        emit this->information(">>> 设置读保护...");
        if(setReadoutProtect(true) < 0)
        {
            ret = -14;
            goto __err;
        }
    }

    // 连接设备
    emit this->information(">>> 连接芯片...");
    if(enterIspMode() < 0)
    {
        ret = -15;
        goto __err;
    }

    emit this->information(">>> 执行复位...");
    exitIspMode();
    emit this->progress(100);
    emit this->information(">>> 烧录完成！");
    emit this->information("");
    emit this->information("");

    goto __exit;

__err:
    emit this->information(QString(">>> 执行失败，请重试！"));

__exit:
    serial->close();
    _isDownloading = false;
}

void QDownloadHandler::msleep(int nms)
{
    QThread::usleep(nms * 1000);
}

int QDownloadHandler::write(const void *buf, quint32 len)
{
    if(!serial->isOpen())
    {
        return -1;
    }
    serial->clear();
    serial->write((const char *)buf, len);
    serial->waitForBytesWritten();
#if 0
    QString s = ">>> ";
    for(quint32 i = 0; i < len; i++)
    {
        s += QString::asprintf("%02X ", *(((quint8 *)buf) + i));
    }
    qDebug() << s;
#endif
    return len;
}

int QDownloadHandler::read(void *buf, quint32 len)
{
    int nread = 0;

    if(!serial->isOpen())
    {
        return -1;
    }

    if(!serial->bytesAvailable() && !serial->waitForReadyRead(1000))
    {
        return -2;
    }

    if((nread = serial->read((char *)buf, len)) <= 0)
    {
        return -3;
    }

#if 0
    QString s = "<<< ";
    for(int i = 0; i < nread; i++)
    {
        s += QString::asprintf("%02X ", *(((quint8 *)buf) + i));
    }
    qDebug() << s;
#endif

    return nread;
}

int QDownloadHandler::goRun(quint32 addr)
{
    char buff[16];

    buff[0] = 0X21;
    buff[1] = 0XDE;
    write(buff, 2);
    read(buff, 1);
    if(buff[0] != ACK)
    {
        return -1;
    }

    buff[0] = (addr >> 24) & 0XFF;
    buff[1] = (addr >> 16) & 0XFF;
    buff[2] = (addr >> 8) & 0XFF;
    buff[3] = (addr >> 0) & 0XFF;
    buff[4] = checksum(buff, 4);
    write(buff, 5);
    read(buff, 1);
    if(buff[0] != ACK)
    {
        return -2;
    }
    return 0;
}

int QDownloadHandler::readPID(quint8 *id, quint8 *len)
{
    /*
[10:54:00.426]发→◇02 FD □
[10:54:00.427]收←◆79 01 04 14 79
*/
    char buff[32];
    int size;

    buff[0] = 0X02;
    buff[1] = 0XFD;
    write(buff, 2);

    msleep(100);

    if((size = read(buff, sizeof(buff))) < 0)
    {
        return -1;
    }

    if(size == 1 || buff[0] != ACK || buff[size - 1] != ACK)  // 接收ACK
    {
        return -2;
    }

    size = buff[1] + 1;
    *len = size;

    for(int i = 0; i < size; i++)
    {
        *id++ = buff[2 + i];
    }

    return 0;
}

int QDownloadHandler::readUID(uint32_t addr, quint8 *id, quint8 *len)
{
    if(readBlock(addr, id, 12) < 0)
    {
        return -1;
    }
    *len = 12;
    return 0;
}

int QDownloadHandler::readFlashCapacity(uint32_t addr, quint32 *capacity)
{
    if(readBlock(addr, capacity, 2) < 0)
    {
        return -1;
    }
    return 0;
}

int QDownloadHandler::readCmd(quint8* protocolVersion, quint8* cmd, quint8* num)
{
/*
[12:33:09.552]发→◇00 FF □
[12:33:09.555]收←◆79 0B 22 00 01 02 11 21 31 43 63 73 82 92 79
*/
    char buff[4];
    int size;

    buff[0] = 0X00;
    buff[1] = 0XFF;
    write(buff, 2);

    msleep(100);

    if(read(&buff[0], 1) < 0 || buff[0] != ACK)  // 读ACK
    {
        return -1;
    }

    if(read(&buff[1], 1) < 0)  // 读数据长度
    {
        return -2;
    }

    if(read(protocolVersion, 1) < 0)  // 读版本号
    {
        return -3;
    }

    buff[1] += 1;

    if((size = read(cmd,  buff[1])) != buff[1])  // 读数据
    {
        return -4;
    }

    *num = buff[1];

    return 0;
}

int QDownloadHandler::setReadoutProtect(bool enable)
{
    quint8 buff[2];
    if(enable)
    {
        buff[0] = 0x82;
        buff[1] = 0x7D;
        write(buff, 2);
        read(buff, 1);
        if(buff[0] != ACK)
        {
            return -1;
        }
        read(buff, 1);
        if(buff[0] != ACK)
        {
            return -2;
        }
    }else
    {
        buff[0] = 0x92;
        buff[1] = 0x6D;
        write(buff, 2);
        read(buff, 1);
        if(buff[0] != ACK)
        {
            return -1;
        }
        read(buff, 1);
        if(buff[0] != ACK)
        {
            return -2;
        }
    }
    return 0;
}

int QDownloadHandler::setWriteProtect(bool enable)
{
    quint8 buff[2];
    if(enable)
    {
        return -1;
    }else
    {
        buff[0] = 0x73;
        buff[1] = 0x8C;
        write(buff, 2);
        read(buff, 1);
        if(buff[0] != ACK)
        {
            return -1;
        }
        read(buff, 1);
        if(buff[0] != ACK)
        {
            return -2;
        }
    }
    return 0;
}

int QDownloadHandler::enterIspMode()      // DTR低电平复位，RTS高电平进Bootloader
{
    serial->setDataTerminalReady(false);  // 拉低DTR复位
    serial->setRequestToSend(true);       // 拉高RTS选择BOOT0=1
    QThread::usleep(100000);              // 延时等待
    serial->setDataTerminalReady(true);   // 拉高DTR释放复位
    QThread::usleep(100000);              // 延时等待
    return connectToDevice();             // 连接设备
}

void QDownloadHandler::exitIspMode()
{
    serial->setDataTerminalReady(false);  // 拉低DTR复位
    serial->setRequestToSend(false);      // 拉低RTS选择BOOT0=0
    QThread::usleep(100000);              // 延时等待
    serial->setDataTerminalReady(true);   // 拉高DTR释放复位
    QThread::usleep(100000);              // 延时等待
}

int QDownloadHandler::connectToDevice()
{
    char chr = 0X7F;
    write(&chr, 1);
    read(&chr, 1);
    if(chr != ACK)
    {
        return -1;
    }
    return 0;
}

int QDownloadHandler::eraseFull()
{
    char buff[2];

    buff[0] = 0X43;
    buff[1] = 0XBC;
    write(buff, 2);
    read(buff, 1);
    if(buff[0] != ACK)
    {
        return -1;
    }

    buff[0] = 0XFF;
    buff[1] = 0X00;
    write(buff, 2);
    read(buff, 1);
    if(buff[0] != ACK)
    {
        return -2;
    }

    return 0;
}

int QDownloadHandler::writeBlock(quint32 addr, const void *buf, quint32 len)
{
    char buff[512];

    // 发送协议头
    buff[0] = 0X31;
    buff[1] = 0XCE;
    write(buff, 2);
    read(buff, 1);
    if(buff[0] != ACK)
    {
        return -1;
    }

    // 发送地址
    buff[0] = (addr >> 24) & 0XFF;
    buff[1] = (addr >> 16) & 0XFF;
    buff[2] = (addr >> 8) & 0XFF;
    buff[3] = (addr >> 0) & 0XFF;
    buff[4] = checksum(buff, 4);
    write(buff, 5);
    read(buff, 1);
    if(buff[0] != ACK)
    {
        return -2;
    }

    // 发送数据
    buff[0] = len - 1;
    memcpy(buff + 1, buf, len);
    buff[len + 1] = checksum(buff, len + 1);
    write(buff, len + 2);
    read(buff, 1);
    if(buff[0] != ACK)
    {
        return -3;
    }

    return 0;
}

int QDownloadHandler::readBlock(quint32 addr, void *buf, quint8 len)
{
    char buff[32];

    // 发送协议头
    buff[0] = 0X11;
    buff[1] = 0XEE;
    write(buff, 2);
    read(buff, 1);
    if(buff[0] != ACK)
    {
        return -1;
    }

    // 发送地址
    buff[0] = (addr >> 24) & 0XFF;
    buff[1] = (addr >> 16) & 0XFF;
    buff[2] = (addr >> 8) & 0XFF;
    buff[3] = (addr >> 0) & 0XFF;
    buff[4] = checksum(buff, 4);
    write(buff, 5);
    read(buff, 1);
    if(buff[0] != ACK)
    {
        return -2;
    }

    // 发送长度
    buff[0] = len - 1;
    buff[1] = ~buff[0];
    write(buff, 2);

    msleep(100);

    // 接收ACK
    read(buff, 1);
    if(buff[0] != ACK)
    {
        return -3;
    }

    // 接收数据
    if(read(buf, len) != len)
    {
        return -4;
    }

    return 0;
}

quint8 QDownloadHandler::checksum(const void *buf, quint32 len)
{
    const quint8* p = (const quint8 *)buf;
    quint8 checkval = 0;
    while(len--)
    {
        checkval ^= *p++;
    }
    return checkval;
}
